package main

import (
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/ngaut/log"
	"global/function"
	"global/structs"
	"time"
	// "github.com/kataras/iris"
	"github.com/kataras/iris"
)

type WorkerStatus int

const (
	WORK_STATUS_STOPED  = 0
	WORK_STATUS_RUNNING = 1
)

type taskTypeAndData struct {
	TasktType 		structs.TaskType
	TaskData 		structs.TrustCoin
}

type Worker struct {
	coinPair structs.CoinPair
	ctx      *Context
	status   WorkerStatus

	startTime 	time.Time
	stopTime 	time.Time

	stop 		chan bool

	isTaskBuyWorking 		bool
	isTaskSaleWorking 		bool
	isTaskCancelWorking 	bool

	timerCheckOrder 		*time.Ticker
}

func NewWorker(ctx *Context, coinPair structs.CoinPair) *Worker {
	return &Worker{
		ctx:      				ctx,
		coinPair: 				coinPair,
		timerCheckOrder: 		time.NewTicker(time.Second * 10),
	}
}

func (w *Worker) Start() {

	w.status = WORK_STATUS_RUNNING
	w.startTime = time.Now()
	w.stop = make(chan bool, 1)

	go func() {
		for {
			select {
			case <-w.timerCheckOrder.C:
				trimOrderList(w.ctx, w.coinPair.Name)
			}
		}
	}()

	iris.RegisterOnInterrupt(func() {
		w.timerCheckOrder.Stop()
	})

	w.serv()
}

func (w *Worker) handlerCancel(tc *structs.Results) {

	defer func() {
		if err := recover(); err != nil {
			w.logError("some error happened when handler cancel trust")
		}
	}()

	handler := NewHandlerDB(w.coinPair)
	handler.Cancel(*tc.ActiveTrust.OriginalTrustCoin)
}

func (w *Worker) handlerResult(results *structs.Results) {
	defer func() {
		if err := recover(); err != nil {
			w.logError("some wrong happened when handler result, %v", err)
		}
	}()

	saveHandler := NewHandlerDB(w.coinPair)
	if !saveHandler.Save(results) {
		w.logInfo("start rebuild redis")
		// 重塑Redis数据
		rebuildRedisData(w.ctx, results, w.coinPair.Name)
		w.logInfo("rebuild redis success")
		return
	}

	conn := w.ctx.GetOteRedis().Get()
	defer conn.Close()

	conn.Send("MULTI")
	if _, err := saveOrdersToRedis(conn, results.ActiveTrust.TrustId, w.coinPair.Name, results.ActiveTrustType, results.OrderLogs); err != nil {
		log.Error("save orders to redis failed")
		conn.Do("DISCARD")
		return
	}
	conn.Do("EXEC")
}

func (w *Worker) handlerTask(handler Handler, queue taskTypeAndData) {
	switch queue.TaskData.Flag {
	case string(structs.TASK_BUY):
		w.isTaskBuyWorking = true
	case string(structs.TASK_SALE):
		w.isTaskSaleWorking = true
	case string(structs.TASK_CANCEL):
		w.isTaskCancelWorking = true
	}

	defer func() {
		switch queue.TaskData.Flag {
		case string(structs.TASK_BUY):
			w.isTaskBuyWorking = false
		case string(structs.TASK_SALE):
			w.isTaskSaleWorking = false
		case string(structs.TASK_CANCEL):
			w.isTaskCancelWorking = false
		}

		if err := recover(); err != nil {
			w.logError("some error happened when handler the task, %v", err)
		}
	}()

	w.logInfo("start handler task, task type: %s, tid: %d", queue.TasktType, queue.TaskData.Id)
	handler.handler(queue.TaskData, queue.TasktType, w)
}

func (w *Worker) serv() {
	go func() {
		conn := w.ctx.GetOteRedis().Get()
		defer conn.Close()

		for {
			if !w.isRun() {
				<-w.stop
				continue
			}

			res, err := redis.Values(conn.Do("BLPOP", function.GetQueueKey(w.coinPair.Name), 0))
			if err != nil {
				w.logError("get trust worker error, msg: %s", err.Error())
				continue
			}

			var str []string
			redis.ScanSlice(res, &str)
			if len(str) < 2 {
				w.logError("get trust data error, value: %v", res)
				continue
			}

			q, err := w.decode(str[1])
			if err != nil {
				log.Errorf("json decode trust error, msg: %s, res: %v", err.Error(), res)
				continue
			}

			w.logInfo("")
			w.logInfo("---------------------------------------------------")
			w.logInfo("  GET TASK FROM QUEUE")
			w.logInfo("---------------------------------------------------")
			w.logInfo("task type: %s, tid: %d, uid: %d", q.Type, q.Data.Id, q.Data.Uid)

			w.dispatch(q)
		}
	}()
}

func (w *Worker) dispatch(queue structs.Queue) {
	trustData, err := transQueueDataToTrustCoin(queue, w.coinPair)
	if err != nil {
		w.logError("trans queue data error, msg: %s", err.Error())
		return
	}

	if queue.Type != structs.TASK_CANCEL {
		err = w.saveToUserAndDetail(trustData)
		if err != nil {
			w.logError("save user and detail error, msg: %s", err.Error())
			return
		}
	}

	task := taskTypeAndData{
		TasktType: 		queue.Type,
		TaskData: 		*trustData,
	}

	switch queue.Type {
	case structs.TASK_BUY:
		w.handlerTask(new(BuyHandler), task)
	case structs.TASK_SALE:
		w.handlerTask(new(SaleHandler), task)
	case structs.TASK_CANCEL:
		if trustData.Flag == string(structs.TASK_BUY) {
			w.handlerTask(new(BuyHandler), task)
		} else {
			w.handlerTask(new(SaleHandler), task)
		}
	}
}

func (w *Worker) saveToUserAndDetail(coin *structs.TrustCoin) error {
	conn := w.ctx.GetOteRedis().Get()
	defer conn.Close()
	conn.Send("MULTI")
	_, err := updateTrustAndUserDetailAndWithMulti(conn, w.coinPair.Name, structs.TaskType(coin.Flag), coin.Id, coin.Uid, coin)
	conn.Do("EXEC")
	return err
}

func (w Worker) logInfo(format string, v ...interface{})  {
	log.Infof("["+ w.coinPair.Name +"] " + format, v...)
}

func (w Worker) logError(format string, v ...interface{})  {
	log.Errorf("["+ w.coinPair.Name +"] " + format, v...)
}

// 获取队列任务数
func (w *Worker) GetQueueNumbers() int {
	conn := w.ctx.GetOteRedis().Get()
	defer conn.Close()
	key := function.GetQueueKey(w.coinPair.Name)
	num, err := redis.Int(conn.Do("LLEN", key))
	if err != nil {
		w.logError("get queue number error, msg: %s", err.Error())
		return 0
	}
	return num
}

func (w Worker) decode(data string) (structs.Queue, error) {
	var queue structs.Queue
	err := json.Unmarshal([]byte(data), &queue)
	return queue, err
}

func (w Worker) isRun() bool {
	return w.status == WORK_STATUS_RUNNING
}

func (w *Worker) Restart() {
	if !w.isRun() {
		w.status = WORK_STATUS_RUNNING
		w.startTime = time.Now()
		if len(w.stop) == 0 {
			w.stop <- true
		}
	}
}

func (w *Worker) Stop() {
	if w.isRun() {
		w.status = WORK_STATUS_STOPED
		w.stopTime = time.Now()
	}
}